home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / src / globalhist.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  29.6 KB  |  1,012 lines

  1. // MDF - PORT FROM NCSA VERSION 2.1
  2.  
  3. /****************************************************************************
  4.  * NCSA Mosaic for the X Window System                                      *
  5.  * Software Development Group                                               *
  6.  * National Center for Supercomputing Applications                          *
  7.  * University of Illinois at Urbana-Champaign                               *
  8.  * 605 E. Springfield, Champaign IL 61820                                   *
  9.  * mosaic@ncsa.uiuc.edu                                                     *
  10.  *                                                                          *
  11.  * Copyright (C) 1993, Board of Trustees of the University of Illinois      *
  12.  *                                                                          *
  13.  * NCSA Mosaic software, both binary and source (hereafter, Software) is    *
  14.  * copyrighted by The Board of Trustees of the University of Illinois       *
  15.  * (UI), and ownership remains with the UI.                                 *
  16.  *                                                                          *
  17.  * The UI grants you (hereafter, Licensee) a license to use the Software    *
  18.  * for academic, research and internal business purposes only, without a    *
  19.  * fee.  Licensee may distribute the binary and source code (if released)   *
  20.  * to third parties provided that the copyright notice and this statement   *
  21.  * appears on all copies and that no charge is associated with such         *
  22.  * copies.                                                                  *
  23.  *                                                                          *
  24.  * Licensee may make derivative works.  However, if Licensee distributes    *
  25.  * any derivative work based on or derived from the Software, then          *
  26.  * Licensee will (1) notify NCSA regarding its distribution of the          *
  27.  * derivative work, and (2) clearly notify users that such derivative       *
  28.  * work is a modified version and not the original NCSA Mosaic              *
  29.  * distributed by the UI.                                                   *
  30.  *                                                                          *
  31.  * Any Licensee wishing to make commercial use of the Software should       *
  32.  * contact the UI, c/o NCSA, to negotiate an appropriate license for such   *
  33.  * commercial use.  Commercial use includes (1) integration of all or       *
  34.  * part of the source code into a product for sale or license by or on      *
  35.  * behalf of Licensee to third parties, or (2) distribution of the binary   *
  36.  * code or source code to third parties that need it to utilize a           *
  37.  * commercial product sold or licensed by or on behalf of Licensee.         *
  38.  *                                                                          *
  39.  * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR   *
  40.  * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED          *
  41.  * WARRANTY.  THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE    *
  42.  * USERS OF THIS SOFTWARE.                                                  *
  43.  *                                                                          *
  44.  * By using or copying this Software, Licensee agrees to abide by the       *
  45.  * copyright law and all other applicable laws of the U.S. including, but   *
  46.  * not limited to, export control laws, and the terms of this license.      *
  47.  * UI shall have the right to terminate this license immediately by         *
  48.  * written notice upon Licensee's breach of, or non-compliance with, any    *
  49.  * of its terms.  Licensee may be held legally responsible for any          *
  50.  * copyright infringement that is caused or encouraged by Licensee's        *
  51.  * failure to abide by the terms of this license.                           *
  52.  *                                                                          *
  53.  * Comments and questions are welcome and can be sent to                    *
  54.  * mosaic-x@ncsa.uiuc.edu.                                                  *
  55.  ****************************************************************************/
  56.  
  57. #include "includes.h"
  58. #include "HTML.h"   /* needed for cache, but should that be here */
  59. #include "mosaic.h"
  60. #include "globals.h"
  61. #include "htmlgad.h"
  62. #include "gui.h"
  63. #include "XtoI.h"
  64. #include "protos.h"
  65. #include <time.h>
  66.  
  67. extern char *cached_url;
  68.  
  69. #define TRACE 0
  70.  
  71. /* Old compilers choke on const... */
  72. #ifdef __STDC__
  73. #define MO_CONST const
  74. #else
  75. #define MO_CONST
  76. #endif /* not __STDC */
  77.  
  78. /* ------------------------------------------------------------------------ */
  79. /* ---------------------------- GLOBAL HISTORY ---------------------------- */
  80. /* ------------------------------------------------------------------------ */
  81.  
  82. /* We save history list out to a file (~/.mosaic-global-history) and
  83.    reload it on entry.
  84.  
  85.    Initially the history file format will look like this:
  86.  
  87.    ncsa-mosaic-history-format-1            [identifying string]
  88.    Global                                  [title]
  89.    url Fri Sep 13 00:00:00 1986            [first word is url;
  90.                                             subsequent words are
  91.                                             last-accessed date (GMT)]
  92.    [1-line sequence for single document repeated as necessary]
  93.    ...
  94. */
  95.  
  96. #define NCSA_HISTORY_FORMAT_COOKIE_ONE "ncsa-mosaic-history-format-1"
  97.  
  98. #define HASH_TABLE_SIZE 200
  99.  
  100. /* Cached data in a hash entry for a given URL; one or both
  101.    slots can be filled; non-filled slots will be NULL. */
  102. typedef struct cached_data
  103. {
  104.   ImageInfo *image_data;
  105.   char *local_name;
  106.   int last_access;
  107. } cached_data;
  108.  
  109. /* An entry in a hash bucket, containing a URL (in canonical,
  110.    absolute form) and possibly cached info (right now, an ImageInfo
  111.    struct for inlined images). */
  112. typedef struct entry
  113. {
  114.   /* Canonical URL for this document. */
  115.   char *url;
  116.   /* This can be one of a couple of things:
  117.      for an image, it's the ImageInfo struct;
  118.      for an HDF file, it's the local filename (if any) */
  119.   cached_data *cached_data;
  120.   struct entry *next;
  121. } entry;
  122.  
  123. /* A bucket in the hash table; contains a linked list of entries. */
  124. typedef struct bucket
  125. {
  126.   entry *head;
  127.   int count;
  128. } bucket;
  129.  
  130. static bucket hash_table[HASH_TABLE_SIZE];
  131.  
  132. static mo_status mo_cache_image_data (cached_data *cd, void *info);
  133. static mo_status mo_uncache_image_data (cached_data *cd);
  134. static int mo_kbytes_in_image_data (void *image_data);
  135.  
  136. static int access_counter = 0;
  137. static int dont_nuke_after_me = 0;
  138. static int kbytes_cached = 0;
  139.  
  140. /* Given a URL, hash it and return the hash value, mod'd by the size
  141.    of the hash table. */
  142. static int hash_url (char *url)
  143. {
  144.   int len, i, val;
  145.  
  146.   if (!url)
  147.     return 0;
  148.   len = strlen (url);
  149.   val = 0;
  150.   for (i = 0; i < 10; i++)
  151.     val += url[(i * val + 7) % len];
  152.  
  153.   return val % HASH_TABLE_SIZE;
  154. }
  155.  
  156. #ifdef DEBUG
  157. /* Each bucket in the hash table maintains a count of the number of
  158.    entries contained within; this function dumps that information
  159.    out to stdout. */
  160. static void dump_bucket_counts (void)
  161. {
  162.   int i;
  163.  
  164.   for (i = 0; i < HASH_TABLE_SIZE; i++)
  165.     fprintf (stdout, "Bucket %03d, count %03d\n", i, hash_table[i].count);
  166.   
  167.   return;
  168. }
  169. #endif /* DEBUG */
  170.  
  171. /* Assume url isn't already in the bucket; add it by
  172.    creating a new entry and sticking it at the head of the bucket's
  173.    linked list of entries. */
  174. static void add_url_to_bucket (int buck, char *url)
  175. {
  176.   bucket *bkt = &(hash_table[buck]);
  177.   entry *l;
  178.  
  179.   l = (entry *)malloc (sizeof (entry));
  180.   l->url = strdup (url);
  181.   l->cached_data = NULL;
  182.   l->next = NULL;
  183.   
  184.   if (bkt->head == NULL)
  185.     bkt->head = l;
  186.   else
  187.     {
  188.       l->next = bkt->head;
  189.       bkt->head = l;
  190.     }
  191.  
  192.   bkt->count += 1;
  193. }
  194.  
  195. /* This is the internal predicate that takes a URL, hashes it,
  196.    does a search through the appropriate bucket, and either returns
  197.    1 or 0 depending on whether we've been there. */
  198. static int been_here_before (char *url)
  199. {
  200.   int hash = hash_url (url);
  201.   entry *l;
  202.  
  203.   if (hash_table[hash].count)
  204.     for (l = hash_table[hash].head; l != NULL; l = l->next)
  205.       {
  206.         if (!strcmp (l->url, url))
  207.           return 1;
  208.       }
  209.   
  210.   return 0;
  211. }
  212.  
  213.  
  214. /* ------------------------------------------------------------------------ */
  215. /* ------------------------------------------------------------------------ */
  216. /* ------------------------------------------------------------------------ */
  217.  
  218. /****************************************************************************
  219.  * name:    mo_been_here_before_huh_dad
  220.  * purpose: Predicate to determine if we've visited this URL before.
  221.  * inputs:  
  222.  *   - char *url: The URL.
  223.  * returns: 
  224.  *   mo_succeed if we've been here before; mo_fail otherwise
  225.  * remarks: 
  226.  *   We canonicalize the URL (stripping out the target anchor, 
  227.  *   if one exists).
  228.  ****************************************************************************/
  229. mo_status mo_been_here_before_huh_dad (char *url)
  230. {
  231.   char *curl = mo_url_canonicalize (url, "");
  232.   mo_status status;
  233.  
  234.   if (been_here_before (curl))
  235.     status = mo_succeed;
  236.   else
  237.     status = mo_fail;
  238.  
  239.   free (curl);
  240.   return status;
  241. }
  242.  
  243.  
  244. /****************************************************************************
  245.  * name:    mo_here_we_are_son
  246.  * purpose: Add a URL to the global history, if it's not already there.
  247.  * inputs:  
  248.  *   - char *url: URL to add.
  249.  * returns: 
  250.  *   mo_succeed
  251.  * remarks: 
  252.  *   We canonicalize the URL (stripping out the target anchor, 
  253.  *   if one exists).
  254.  ****************************************************************************/
  255. mo_status mo_here_we_are_son (char *url)
  256. {
  257.   char *curl = mo_url_canonicalize (url, "");
  258.  
  259.   if (!been_here_before (curl))
  260.     add_url_to_bucket (hash_url (curl), curl);
  261.   
  262.   free (curl);
  263.  
  264.   return mo_succeed;
  265. }
  266.  
  267.  
  268. /****************************************************************************
  269.  * name:    mo_read_global_history (PRIVATE)
  270.  * purpose: Given a filename, read the file's contents into the
  271.  *          global history hash table.
  272.  * inputs:  
  273.  *   - char *filename: The file to read.
  274.  * returns: 
  275.  *   nothing
  276.  * remarks: 
  277.  *   
  278.  ****************************************************************************/
  279. static void mo_read_global_history (char *filename)
  280. {
  281.   FILE *fp;
  282.   char line[MO_LINE_LENGTH];
  283.   char *status;
  284.   
  285.   fp = fopen (filename, "r");
  286.   if (!fp)
  287.     goto screwed_no_file;
  288.   
  289.   status = fgets (line, MO_LINE_LENGTH, fp);
  290.   if (!status || !(*line))
  291.     goto screwed_with_file;
  292.   
  293.   /* See if it's our format. */
  294.   if (strncmp (line, NCSA_HISTORY_FORMAT_COOKIE_ONE,
  295.                strlen (NCSA_HISTORY_FORMAT_COOKIE_ONE)))
  296.     goto screwed_with_file;
  297.  
  298.   /* Go fetch the name on the next line. */
  299.   status = fgets (line, MO_LINE_LENGTH, fp);
  300.   if (!status || !(*line))
  301.     goto screwed_with_file;
  302.   
  303.   /* Start grabbing url's. */
  304.   while (1)
  305.     {
  306.       char *url;
  307.       
  308.       status = fgets (line, MO_LINE_LENGTH, fp);
  309.       if (!status || !(*line))
  310.         goto done;
  311.       
  312.       url = strtok (line, " ");
  313.       if (!url)
  314.         goto screwed_with_file;
  315.  
  316.       /* We don't use the last-accessed date... yet. */
  317.       /* lastdate = strtok (NULL, "\n"); blah blah blah... */
  318.  
  319.       add_url_to_bucket (hash_url (url), url);
  320.     }
  321.   
  322.  screwed_with_file:
  323.  done:
  324.   fclose (fp);
  325.   return;
  326.  
  327.  screwed_no_file:
  328.   return;
  329. }
  330.  
  331.  
  332.  
  333. /****************************************************************************
  334.  * name:    mo_init_global_history
  335.  * purpose: Initialize the global history hash table.
  336.  * inputs:  
  337.  *   none
  338.  * returns: 
  339.  *   mo_succeed
  340.  * remarks: 
  341.  *   
  342.  ****************************************************************************/
  343. mo_status mo_init_global_history (void)
  344. {
  345.   int i;
  346.  
  347.   /* Initialize the history structs. */
  348.   for (i = 0; i < HASH_TABLE_SIZE; i++)
  349.     {
  350.       hash_table[i].count = 0;
  351.       hash_table[i].head = 0;
  352.     }
  353.  
  354.   return mo_succeed;
  355. }
  356.  
  357.  
  358. /****************************************************************************
  359.  * name:    mo_wipe_global_history
  360.  * purpose: Wipe out the current global history.
  361.  * inputs:  
  362.  *   none
  363.  * returns: 
  364.  *   mo_succeed
  365.  * remarks: 
  366.  *   Huge memory hole here.  However, we now call
  367.  *   mo_flush_image_cache to at least clear out the image structures.
  368.  ****************************************************************************/
  369. mo_status mo_wipe_global_history (mo_window *win)
  370. {
  371.   mo_flush_image_cache (win);
  372.  
  373.   /* Memory leak! @@@ */
  374.   mo_init_global_history ();
  375.  
  376.   return mo_succeed;
  377. }
  378.  
  379.  
  380. /****************************************************************************
  381.  * name:    mo_setup_global_history
  382.  * purpose: Called on program startup to do the global history
  383.  *          initialization stuff, including figuring out where the
  384.  *          global history file is and reading it.
  385.  * inputs:  
  386.  *   none
  387.  * returns: 
  388.  *   mo_succeed
  389.  * remarks: 
  390.  *   
  391.  ****************************************************************************/
  392. static char *cached_global_hist_fname = NULL;
  393. mo_status mo_setup_global_history (void)
  394. {
  395.   char *home = getenv ("HOME");
  396.   char *default_filename = Rdata.global_history_file;
  397.   char *filename;
  398.  
  399.   mo_init_global_history ();
  400.  
  401.   /* This shouldn't happen. */
  402.   if (!home)
  403.      home = "t:";
  404.   
  405.   filename = (char *)malloc 
  406.     ((strlen (home) + strlen (default_filename) + 8) * sizeof (char));
  407. //  sprintf (filename, "%s/%s\0", home, default_filename);
  408.   sprintf (filename, "%s\0", default_filename);
  409.   cached_global_hist_fname = filename;
  410.  
  411.   mo_read_global_history (filename);
  412.   
  413.   return mo_succeed;
  414. }
  415.  
  416.  
  417. /****************************************************************************
  418.  * name:    mo_write_global_history
  419.  * purpose: Write the global history file out to disk.
  420.  * inputs:  
  421.  *   none
  422.  * returns: 
  423.  *   mo_succeed (usually)
  424.  * remarks: 
  425.  *   This assigns last-read times to all the entries in the history,
  426.  *   which is a bad thing.
  427.  ****************************************************************************/
  428. mo_status mo_write_global_history (void)
  429. {
  430.   FILE *fp;
  431.   int i;
  432.   entry *l;
  433.   time_t foo = time (NULL);
  434.   char *ts = ctime (&foo);
  435.  
  436.   ts[strlen(ts)-1] = '\0';
  437.  
  438.   fp = fopen (cached_global_hist_fname, "w");
  439.   if (!fp)
  440.     return mo_fail;
  441.  
  442.   fprintf (fp, "%s\n%s\n", NCSA_HISTORY_FORMAT_COOKIE_ONE, "Global");
  443.   
  444.   for (i = 0; i < HASH_TABLE_SIZE; i++)
  445.     {
  446.       for (l = hash_table[i].head; l != NULL; l = l->next)
  447.         {
  448.           fprintf (fp, "%s %s\n", l->url, ts);
  449.         }
  450.     }
  451.   
  452.   fclose (fp);
  453.   
  454.   return mo_succeed;
  455. }
  456.  
  457.  
  458. /****************************************************************************
  459.  * name:    mo_fetch_cached_image_data
  460.  * purpose: Retrieve a piece of cached data associated with a URL.
  461.  * inputs:  
  462.  *   - char *url: The URL.
  463.  * returns: 
  464.  *   The piece of cached data (void *).
  465.  * remarks: 
  466.  *   We do *not* do anything to the URL.  If there is a target
  467.  *   anchor in it, fine with us.  This means the target anchor
  468.  *   should have been stripped out someplace else if it needed to be.
  469.  ****************************************************************************/
  470. void *mo_fetch_cached_image_data (char *url)
  471. {
  472.   int hash = hash_url (url);
  473.   entry *l;
  474.  
  475.   if (hash_table[hash].count)
  476.     for (l = hash_table[hash].head; l != NULL; l = l->next)
  477.       {
  478.         if (!strcmp (l->url, url))
  479.           {
  480.             if (l->cached_data && l->cached_data->image_data)
  481.               {
  482.                 if (TRACE)
  483. //                  fprintf (stderr, "[mo_fetch_cached_image_data] Hit for '%s', data 0x%08x\n", url, l->cached_data->image_data);
  484.                 l->cached_data->last_access = access_counter++;
  485.                 return l->cached_data->image_data;
  486.               }
  487.             else
  488.               {
  489.                 if (TRACE)
  490. //                  fprintf (stderr, "[mo_fetch_cached_image_data] Miss for '%s'\n",
  491. //                         url);
  492.                 return NULL;
  493.               }
  494.           }
  495.       }
  496.   
  497.   return NULL;
  498. }
  499.  
  500. #ifdef DEBUG
  501. /****************************************************************************
  502.  * name:    mo_fetch_cached_local_name
  503.  * purpose: Retrieve a piece of cached data associated with a URL.
  504.  * inputs:  
  505.  *   - char *url: The URL.
  506.  * returns: 
  507.  *   The piece of cached data (void *).
  508.  * remarks: 
  509.  *   We do *not* do anything to the URL.  If there is a target
  510.  *   anchor in it, fine with us.  This means the target anchor
  511.  *   should have been stripped out someplace else if it needed to be.
  512.  ****************************************************************************/
  513. void *mo_fetch_cached_local_name (char *url)
  514. {
  515.   int hash = hash_url (url);
  516.   entry *l;
  517.  
  518.   if (hash_table[hash].count)
  519.     for (l = hash_table[hash].head; l != NULL; l = l->next)
  520.       {
  521.         if (!strcmp (l->url, url))
  522.           {
  523.             if (l->cached_data)
  524.               return l->cached_data->local_name;
  525.             else
  526.               return NULL;
  527.           }
  528.       }
  529.   
  530.   return NULL;
  531. }
  532. #endif
  533.  
  534.  
  535. /****************************************************************************
  536.  * name:    mo_cache_data
  537.  * purpose: Cache a piece of data associated with a given URL.
  538.  * inputs:  
  539.  *   - char  *url: The URL.
  540.  *   - void *info: The piece of data to cache (currently either
  541.  *                 an ImageInfo struct for an image named as SRC
  542.  *                 in an IMG tag, or the filename corresponding to the
  543.  *                 local copy of a remote HDF file).
  544.  *   - int   type: The type of data to cache (currently either
  545.  *                 0 for an ImageInfo struct or 1 for a local name).
  546.  * returns: 
  547.  *   mo_succeed, unless something goes badly wrong
  548.  * remarks: 
  549.  *   We do *not* do anything to the URL.  If there is a target
  550.  *   anchor in it, fine with us.  This means the target anchor
  551.  *   should have been stripped out someplace else if it needed to be.
  552.  ****************************************************************************/
  553. mo_status mo_cache_data (char *url, void *info, int type)
  554. {
  555.   int hash = hash_url (url);
  556.   entry *l;
  557.  
  558.   /* First, register ourselves if we're not already registered.
  559.      Now, the same URL can be registered multiple times with different
  560.      (or, in one instance, no) internal anchor. */
  561.   if (!been_here_before (url))
  562.     add_url_to_bucket (hash_url (url), url);
  563.  
  564.   /* Then, find the right entry. */
  565.   if (hash_table[hash].count)
  566.     for (l = hash_table[hash].head; l != NULL; l = l->next)
  567.       {
  568.         if (!strcmp (l->url, url))
  569.           goto found;
  570.       }
  571.   
  572.   return mo_fail;
  573.  
  574.  found:
  575.   if (!l->cached_data)
  576.     {
  577.       l->cached_data = (cached_data *)malloc (sizeof (cached_data));
  578.       l->cached_data->image_data = NULL;
  579.       l->cached_data->local_name = NULL;
  580.       l->cached_data->last_access = 0;
  581.     }
  582.  
  583.   if (type == 0)
  584.     {
  585.       if (TRACE)
  586.         fprintf (stderr, "[mo_cache_data] Caching '%s', data 0x%08x\n",
  587.                  url, info);
  588.       mo_cache_image_data (l->cached_data, info);
  589.     }
  590.   else if (type == 1)
  591.     l->cached_data->local_name = info;
  592.  
  593.   return mo_succeed;
  594. }
  595.  
  596.  
  597. mo_status mo_zap_cached_images_here (mo_window *win)
  598. {
  599.   char **hrefs;
  600.   int num;
  601.   void *ptr;
  602. /*  HTMLPart *inst;*/
  603.  
  604.   /* Go fetch new hrefs. */
  605. /*  get(HTML_gad, HTML_inst, (ULONG *)(&inst)); */
  606. /*  hrefs = HTMLGetImageSrcs (inst, &(num)); */
  607.   hrefs = TO_HTMLGetImageSrcs (win, &(num));
  608.  
  609.   if (num)
  610.     {
  611.       int i;
  612.       for (i = 0; i < num; i++)
  613.         {
  614.           char *url = mo_url_canonicalize (hrefs[i], cached_url);
  615.           ptr = mo_fetch_cached_image_data (url);
  616.           if (ptr)
  617.             {
  618.               mo_cache_data (url, NULL, 0);
  619.             }
  620.         }
  621.  
  622.       /* All done; clean up. */
  623.       for (i = 0; i < num; i++)
  624.         free (hrefs[i]);
  625.       free (hrefs);
  626.     }
  627.  
  628.   return mo_succeed;
  629. }
  630.  
  631.  
  632. /****************************************************************************
  633.  * name:    mo_flush_image_cache
  634.  * purpose: 
  635.  * inputs:  
  636.  *   - mo_window *win: The current window.
  637.  * returns: 
  638.  *   nuthin
  639.  * remarks: 
  640.  ****************************************************************************/
  641. mo_status mo_flush_image_cache (mo_window *win)
  642. {
  643.   entry *l;
  644.   int hash;
  645.  
  646.   for (hash = 0; hash < HASH_TABLE_SIZE; hash++)
  647.     for (l = hash_table[hash].head; l != NULL; l = l->next)
  648.       if (l->cached_data)
  649.         if (l->cached_data->image_data)
  650.           {
  651.             mo_uncache_image_data (l->cached_data);
  652.           }
  653.  
  654.   return mo_succeed;
  655. }
  656.  
  657.  
  658. /* ------------------------------------------------------------------------ */
  659. /* ------------------------- decent image caching ------------------------- */
  660. /* ------------------------------------------------------------------------ */
  661.  
  662. /* CHUNK_OF_IMAGES determines the initial size of the array of cached
  663.    pointers to image data; if more images must be cached, the array is
  664.    grown with realloc by this amount.  It is good to keep the array as
  665.    small as possible, as it must occasionally be sorted. */
  666. #define CHUNK_OF_IMAGES 10
  667.  
  668. static cached_data **cached_cd_array = NULL;
  669. static int num_in_cached_cd_array = 0;
  670. static int size_of_cached_cd_array = 0;
  671.  
  672. static mo_status mo_dump_cached_cd_array (void)
  673. {
  674.   int i;
  675.   if (!cached_cd_array)
  676.     {
  677.       fprintf (stderr, "[mo_dump_cached_cd_array] No array; punting\n");
  678.       return mo_fail;
  679.     }
  680.  
  681.   fprintf (stderr, "+++++++++++++++++++++++++\n");
  682.   for (i = 0; i < size_of_cached_cd_array; i++)
  683.     {
  684.       if (cached_cd_array[i])
  685.         fprintf (stderr, "  %02d data 0x%08x last_access %d\n", i,
  686.                  cached_cd_array[i]->image_data, 
  687.                  cached_cd_array[i]->last_access);
  688.       else
  689.         fprintf (stderr, "  %02d NULL\n", i);
  690.     }
  691.   fprintf (stderr, "---------------------\n");
  692.  
  693.   return mo_succeed;
  694. }
  695.  
  696. static mo_status mo_init_cached_cd_array (void)
  697. {
  698.   cached_cd_array = (cached_data **)malloc (sizeof (cached_data *) * 
  699.                                             CHUNK_OF_IMAGES);
  700.   size_of_cached_cd_array += CHUNK_OF_IMAGES;
  701.  
  702.   if (TRACE)
  703.     fprintf (stderr, "[mo_init] Did it 0x%08x -- allocated %d pointers.\n",
  704.              cached_cd_array,
  705.              size_of_cached_cd_array);
  706.  
  707.   bzero ((char *)cached_cd_array, 
  708.          CHUNK_OF_IMAGES * sizeof (cached_cd_array[0]));
  709.  
  710.   return mo_succeed;
  711. }
  712.  
  713.  
  714. static mo_status mo_grow_cached_cd_array (void)
  715. {
  716.   cached_cd_array = (cached_data **)realloc 
  717.     (cached_cd_array,
  718.      sizeof (cached_data *) * (size_of_cached_cd_array + CHUNK_OF_IMAGES));
  719.  
  720.   if (TRACE)
  721.     fprintf (stderr, "[grow] cached_cd_array 0x%08x, size_of_cached_cd_array 0x%08x, sum 0x%08x\n",
  722.              cached_cd_array, size_of_cached_cd_array, 
  723.              cached_cd_array + size_of_cached_cd_array);
  724.   bzero ((char *)(cached_cd_array + size_of_cached_cd_array), 
  725.          CHUNK_OF_IMAGES * sizeof (cached_cd_array[0]));
  726.  
  727.   size_of_cached_cd_array += CHUNK_OF_IMAGES;
  728.  
  729.   return mo_succeed;
  730. }
  731.  
  732. static int mo_sort_cd_for_qsort (MO_CONST void *a1, MO_CONST void *a2)
  733. {
  734.   cached_data **d1 = (cached_data **)a1;
  735.   cached_data **d2 = (cached_data **)a2;
  736.  
  737.   /* NULL entries will be at end of array -- this may be good,
  738.      or may not be -- hmmmmmm. */
  739.   if (!d1 || !(*d1))
  740.     return 1;
  741.   if (!d2 || !(*d2))
  742.     return -1;
  743.  
  744.   if (TRACE)
  745.     fprintf (stderr, "sort: hi there! %d %d\n",
  746.              (*d1)->last_access, (*d2)->last_access);
  747.  
  748.   return ((*d1)->last_access < (*d2)->last_access ? -1 : 1);
  749. }
  750.  
  751. static mo_status mo_sort_cached_cd_array (void)
  752. {
  753.   if (!cached_cd_array)
  754.     {
  755.       if (TRACE)
  756.         fprintf (stderr, "[mo_sort_cached_cd_array] No array; punting\n");
  757.       return mo_fail;
  758.     }
  759.  
  760.   if (num_in_cached_cd_array == 0)
  761.     {
  762.       if (TRACE)
  763.         fprintf (stderr, "[mo_sort_cached_cd_array] Num in array 0; punting\n");
  764.       return mo_fail;
  765.     }
  766.  
  767.   if (TRACE)
  768.     {
  769.       fprintf (stderr, "[mo_sort_cached_cd_array] Sorting 0x%08x!\n",
  770.                cached_cd_array);
  771.       mo_dump_cached_cd_array ();
  772.     }
  773.   qsort 
  774.     ((void *)cached_cd_array, size_of_cached_cd_array, 
  775.      sizeof (cached_cd_array[0]), mo_sort_cd_for_qsort);
  776.   if (TRACE)
  777.     mo_dump_cached_cd_array ();
  778.   
  779.   return mo_succeed;
  780. }
  781.  
  782. static mo_status mo_remove_cd_from_cached_cd_array (cached_data *cd)
  783. {
  784.   int i, freed_kbytes = 0;
  785.   
  786.   if (!cached_cd_array)
  787.     return mo_fail;
  788.  
  789.   for (i = 0; i < size_of_cached_cd_array; i++)
  790.     {
  791.       if (cached_cd_array[i] == cd)
  792.         {
  793.           if (TRACE)
  794.             fprintf 
  795.               (stderr, 
  796.                "[mo_remove_cd_from_cached_cd_array] Found data 0x%08x, location %d\n", 
  797.                cached_cd_array[i]->image_data, i);
  798.           freed_kbytes = mo_kbytes_in_image_data 
  799.             (cached_cd_array[i]->image_data);
  800.           mo_free_image_data (cached_cd_array[i]->image_data);
  801.           cached_cd_array[i] = NULL;
  802.           goto done;
  803.         }
  804.     }
  805.  
  806.   if (TRACE)
  807.     fprintf (stderr, "[mo_remove_cd] UH OH, DIDN'T FIND IT!!!!!!!!!\n");
  808.   return mo_fail;
  809.   
  810.  done:
  811.   num_in_cached_cd_array--;
  812.   kbytes_cached -= freed_kbytes;
  813.   return mo_succeed;
  814. }
  815.  
  816. static mo_status mo_add_cd_to_cached_cd_array (cached_data *cd)
  817. {
  818.   int i, num;
  819.   int kbytes_in_new_image = mo_kbytes_in_image_data (cd->image_data);
  820.   
  821.   if (TRACE)
  822.     fprintf (stderr, "[mo_add_cd] New image is %d kbytes.\n",
  823.              kbytes_in_new_image);
  824.  
  825.   if (!cached_cd_array)
  826.     {
  827.       mo_init_cached_cd_array ();
  828.       if (TRACE)
  829.         fprintf (stderr, "[mo_add_cd] Init'd cached_cd_array.\n");
  830.     }
  831.   else
  832.     {
  833.       /* Maybe it's already in there. */
  834.       for (i = 0; i < size_of_cached_cd_array; i++)
  835.         {
  836.           if (cached_cd_array[i] == cd)
  837.             return mo_succeed;
  838.         }
  839.     }
  840.  
  841.   /* Here's the magic part. */
  842.   if ((kbytes_cached + kbytes_in_new_image) > Rdata.image_cache_size)
  843.     {
  844.       int num_to_remove = 0;
  845.       if (TRACE)
  846.         fprintf (stderr, "[mo_add_cd] Going to sort 0x%08x...\n", 
  847.                  cached_cd_array);
  848.       mo_sort_cached_cd_array ();
  849.       if (TRACE)
  850.         {
  851.           fprintf (stderr, 
  852.                    "[mo_add_to] Just sorted in preparation for purging...\n");
  853.           mo_dump_cached_cd_array ();
  854.         }
  855.       while ((kbytes_cached + kbytes_in_new_image) > Rdata.image_cache_size)
  856.         {
  857.           if (TRACE)
  858.             fprintf(stderr, "[mo_add_cd] Trying to free another image (%d > %d).\n",(kbytes_cached + kbytes_in_new_image), Rdata.image_cache_size);
  859.           /* Try to remove one -- we rely on the fact that NULL
  860.              entries in cached_cd_array are at the end of the array. */
  861.           if (num_to_remove < size_of_cached_cd_array &&
  862.               cached_cd_array[num_to_remove] != NULL)
  863.             {
  864.               if (TRACE)
  865.                 fprintf (stderr, "        ** going to try to remove %d; last_access %d < dont_nuke_after_me %d??\n",cached_cd_array[num_to_remove]->last_access,dont_nuke_after_me);
  866.               if (cached_cd_array[num_to_remove]->last_access <
  867.                   dont_nuke_after_me)
  868.                 {
  869.                   if (TRACE)
  870.                     fprintf (stderr, "        ** really removing %d\n",
  871.                              num_to_remove);
  872.                   mo_uncache_image_data (cached_cd_array[num_to_remove]);
  873.                   if (TRACE)
  874.                     mo_dump_cached_cd_array ();
  875.                 }
  876.               num_to_remove++;
  877.             }
  878.           else
  879.             {
  880.               if (TRACE)
  881.                 fprintf (stderr, "        ** no more to remove\n");
  882.               if (TRACE)
  883.                 mo_dump_cached_cd_array ();
  884.               goto removed_em_all;
  885.             }
  886.         }
  887.     }
  888.  removed_em_all:
  889.   
  890.   if (num_in_cached_cd_array == size_of_cached_cd_array)
  891.     {
  892.       if (TRACE)
  893.         fprintf (stderr, "[mo_add_cd] Growing array... \n");
  894.       num = size_of_cached_cd_array;
  895.       mo_grow_cached_cd_array ();
  896.     }
  897.   else
  898.     {
  899.       num = -1;
  900.       for (i = 0; i < size_of_cached_cd_array; i++)
  901.         {
  902.           if (cached_cd_array[i] == NULL)
  903.             {
  904.               num = i;
  905.               goto got_num;
  906.             }
  907.         }
  908.       if (TRACE)
  909.         fprintf 
  910.           (stderr, 
  911.            "[mo_add_cd_to_cached_cd_array] UH OH couldn't find empty slot\n");
  912.       /* Try to grow array -- flow of control should never reach here,
  913.          though. */
  914.       num = size_of_cached_cd_array;
  915.       mo_grow_cached_cd_array ();
  916.     }
  917.   
  918.  got_num:
  919.   cached_cd_array[num] = cd;
  920.   num_in_cached_cd_array++;
  921.  
  922.   kbytes_cached += kbytes_in_new_image;
  923.  
  924.   if (TRACE)
  925.     {
  926.       fprintf 
  927.         (stderr, 
  928.          "[mo_add_cd_to_cached_cd_array] Added cd, data 0x%08x, num %d\n",
  929.          cd->image_data, num);
  930.       fprintf 
  931.         (stderr,
  932.          "[mo_add_cd_to_cached_cd_array] Now cached %d kbytes.\n", kbytes_cached);
  933.       mo_dump_cached_cd_array ();
  934.     }
  935.  
  936.   return mo_succeed;
  937. }
  938.  
  939. static int mo_kbytes_in_image_data (void *image_data)
  940. {
  941.   ImageInfo *img = (ImageInfo *)image_data;
  942.   int bytes, kbytes;
  943.  
  944.   if (!img)
  945.     return 0;
  946.  
  947.     bytes = (GetBitMapAttr((struct BitMap *)img->image,BMA_HEIGHT) *
  948.             GetBitMapAttr((struct BitMap *)img->image,BMA_WIDTH) *
  949.             GetBitMapAttr((struct BitMap *)img->image,BMA_DEPTH))/8;
  950.  
  951.   kbytes = bytes >> 10;
  952.  
  953.   if (TRACE)
  954.     fprintf (stderr, "[mo_kbytes_in_image_data] bytes %d, kbytes %d\n",
  955.              bytes, kbytes);
  956.  
  957.   if (kbytes == 0)
  958.     kbytes = 1;
  959.   
  960.   return kbytes;
  961. }
  962.  
  963. static mo_status mo_cache_image_data (cached_data *cd, void *info)
  964. {
  965.   /* Beeeeeeeeeeeeeeeeee smart! */
  966.   if (Rdata.image_cache_size <= 0)
  967.     Rdata.image_cache_size = 1;
  968.  
  969.   /* It's possible we'll be getting NULL info here, so we
  970.      should uncache in this case... */
  971.   if (!info)
  972.     mo_uncache_image_data (cd);
  973.  
  974.   cd->image_data = info;
  975.   cd->last_access = access_counter++;
  976.  
  977.   mo_add_cd_to_cached_cd_array (cd);
  978.  
  979.   return mo_succeed;
  980. }
  981.  
  982. static mo_status mo_uncache_image_data (cached_data *cd)
  983. {
  984.   mo_remove_cd_from_cached_cd_array (cd);
  985.  
  986.   if (cd->image_data->image) {
  987. //    puts("freeing bm");
  988. //    FreeBitMap(cd->image_data->image); /* DT does this for now... */
  989.   }
  990.   if (cd->image_data->mask_bitmap) FreeBitMap(cd->image_data->mask_bitmap);
  991.   if (cd->image_data->dt) {
  992. //    puts("freeing dt");
  993.     DisposeDTObject(cd->image_data->dt);
  994.   }
  995.   if (cd->image_data->fname) {
  996.     unlink(cd->image_data->fname);
  997.     free(cd->image_data->fname);
  998.   }
  999.   cd->image_data = NULL;
  1000.  
  1001.   return mo_succeed;
  1002. }
  1003.  
  1004. mo_status mo_set_image_cache_nuke_threshold (void)
  1005. {
  1006.   if (TRACE)
  1007.     fprintf (stderr, "[mo_set_nuke_threshold] Setting to %d\n",
  1008.              access_counter);
  1009.   dont_nuke_after_me = access_counter;
  1010.   return mo_succeed;
  1011. }
  1012.